"""
this is a generic argparser.  The only mandatory input is the planning algorithm
(i.e. fwd, backward, PSP, etc.)   The default is to use FWD search, Iterative Deepening and all levels
"""

import sys
import argparse
import levelsParser
import board
import planningAlgs
import datetime

parser = argparse.ArgumentParser(description="Use a planning algorithm to find a solution to a goop escape level")
parser.add_argument("-p","--planAlg",
                    help="which deterministic planning algorithm to use",
                    choices=["fwd","bwd","psp"],
                    default="fwd")
parser.add_argument("-s","--searchAlg",
                    help="which DET to use if any",
                    choices=["AS","ID","BFS","DFS", "BEST", "FF", "ALL"],
                    default="ALL")
parser.add_argument("-u","--heuristic",
                    help="which Heuristic to use if any",
                    choices=["hrpg","ord-lm","null"],
                    default="hrpg")
parser.add_argument("-d","--depth",
                    help="How far down the search tree goes",
                    default=35,
                    type=int)
#need to add an exception to levelsParser for non-existent levels 
parser.add_argument("-l","--level",
                    help="which level to use (default=all)",
                    default="all")
parser.add_argument("-t","--timeout",
                    help="Select a time out value level to use (default=all)",
                    default=300,
                    type=int)
parser.add_argument("-x","--xtest",
                    help="Run multiple iterations for testing purposes",
                    action='store_true')
parser.add_argument("-v","--verbose",
                    help="1 to print the board and everything. 0 for concise output to be copied to a spreadsheet.",
                    default=0,
                    type=int)
parser.add_argument("-r","--rerun",
                    help="1 to keep rerunning with decreasing depth limit until it timeouts, 0 to run just once with the given max depth.",
                    default=1,
                    type=int)
parser.add_argument("-o","--outputFile",
                    help="Specify name of output file, STDOUT for stdout",
                    #dt =  datetime.datetime.now(),
                    default=datetime.datetime.strftime(datetime.datetime.now(),"%B_%d_%Y_%H:%M") )
                    #print datetime.strftime(datetime.now(),"%B_%d_%Y_%H:%M"))

args = parser.parse_args()

def runLevelWithAlgAndHeuristicAndDepth(lev, searchAlg, heuristic, depth):
    alg = planningAlgs.PlanningAlg(lev, searchAlg, heuristic, depth, args.timeout)
    time, plan = alg.runAlg()
    if args.verbose == 1:
        print >> file1, lev
        print >> file1, 'Plan of length %d:' % len(plan), plan
        print >> file1, "Time to completion was %f seconds" % time
        print >> file1
    return len(plan), time

def runLevelWithAlgAndHeuristic(lev, searchAlg, heuristic, rerun):
    bestPlanLength, bestTime = runLevelWithAlgAndHeuristicAndDepth(lev, searchAlg, heuristic, args.depth)
    file1.flush()
    if args.verbose == 0:
        file1.write(str(bestPlanLength) + '\t' + ('%.3f' % bestTime) + '\t')

    planLength = bestPlanLength
    if rerun == 1:
        while planLength > 1:  # Valid plan (not ["FAIL"])
            planLength, time = runLevelWithAlgAndHeuristicAndDepth(lev, searchAlg, heuristic, planLength - 1)
            if planLength > 1:
                bestPlanLength = planLength
                bestTime = time

        if args.verbose == 0:
            file1.write(str(bestPlanLength) + '\t' + ('%.3f' % bestTime) + '\t')

def runLevel(lev):
    
    if args.searchAlg != "ALL":
        runLevelWithAlgAndHeuristic(lev, args.searchAlg, args.heuristic, rerun=args.rerun)
        print >> file1
    else:
        runLevelWithAlgAndHeuristic(lev, 'BFS', 'null', rerun=0)
        runLevelWithAlgAndHeuristic(lev, 'DFS', 'null', rerun=1)
        runLevelWithAlgAndHeuristic(lev, 'ID', 'null', rerun=0)
        runLevelWithAlgAndHeuristic(lev, 'BEST', 'hrpg', rerun=1)
        runLevelWithAlgAndHeuristic(lev, 'AS', 'hrpg', rerun=1)
        runLevelWithAlgAndHeuristic(lev, 'FF', 'hrpg', rerun=0)
        runLevelWithAlgAndHeuristic(lev, 'ID', 'ord-lm', rerun=0)
        print >> file1

"""
This is a pretty basic shell for using each algorithm.  As we add more algorithms,
we can add options above to the parser and fill out this 'if' statement.

Additionally, there is no code for printing or showing progress. 
"""
if args.outputFile == "STDOUT":
    file1 = sys.stdout
else:
    fname =  args.outputFile+'.tsv'
    file1 = open(fname, 'a')

if args.xtest:

    if args.level == "all":
        runs = {}
        for lev in levelsParser.getAllLevels():
#             print lev
            alg = planningAlgs.PlanningAlg(lev, args.searchAlg, args.heuristic, args.depth, args.timeout)
            runs[lev.level_number] = []
            for i in xrange(10):
                time, plan = alg.runAlg()
                runs[lev.level_number].append((time,len(plan)))
#             print 'Plan of length %d:' % len(plan), plan
#             print "Time to completion was %f seconds" % time
        for l in sorted(runs.keys()):
            print >> file1, l, runs[l]
    else:
        lev = levelsParser.getLevel(args.level)
#         print lev
        alg = planningAlgs.PlanningAlg(lev,args.searchAlg, args.heuristic, args.depth, args.timeout)
        run = []
        for i in xrange(10):
            time, plan = alg.runAlg()
            run.append((time,len(plan)))
        print >> file1, lev.level_number, run
#         print 'Plan of length %d:' % len(plan), plan
#         print "Time to completion was %f seconds" % time       
else:
    if not args.verbose:
        print >> file1, 'BFS sol\t\tDFS first sol\tDFS best sol\tID sol\t\tBestF first sol\tBestF best sol\tA* first sol\tA* best sol\tFF sol\t\tLandMarks sol\t\t'            

    if args.level == "all":
        for lev in levelsParser.getAllLevels():
            runLevel(lev)
    else:
        lev = levelsParser.getLevel(args.level)
        runLevel(lev)

        
